package dao

import (
	"context"
	"encoding/json"
	"fmt"
	"gbox-one-machine/discovery"
	"net/http"
)

// http path
const (
	getBindNodePath = "/api/v1/resource_center/cluster/bind_node/%s"
	setBindNodePath = "/api/v1/resource_center/cluster/bind_node"
	getNodeRolePath = "/api/v1/resource_center/cluster/node_role"
	subNodeRolePath = "/api/v1/resource_center/cluster/node_role?wait=true&timeout=15&id=%s"
)

type bindNodeResp struct {
	ReturnCode   int             `json:"returnCode"`
	ErrorMessage string          `json:"errorMessage"`
	Result       *discovery.Node `json:"result"`
}

// GetBindNode get bind node
func (d *Dao) GetBindNode(ctx context.Context, ip string, port int) (*discovery.Node, error) {

	resResp := new(bindNodeResp)

	ipPort := fmt.Sprintf("%s:%d", ip, port)
	resp, err := d.http().R().SetContext(ctx).Get(d.resCenterHost + fmt.Sprintf(getBindNodePath, ipPort))
	if err != nil {
		return nil, fmt.Errorf("HTTP request err: %s", err)
	}

	if resp.StatusCode() != http.StatusOK {
		if resp.StatusCode() == http.StatusNotFound {
			return nil, nil
		}
		return nil, fmt.Errorf("HTTP status %s", resp.Status())
	}

	if err := json.Unmarshal(resp.Body(), resResp); err != nil {
		return nil, err
	}

	if resResp.ReturnCode != 0 {
		return nil, fmt.Errorf("Errcode: %d, Msg: %s", resResp.ReturnCode, resResp.ErrorMessage)
	}

	return resResp.Result, nil
}

type setBindNodeResp struct {
	ReturnCode   int      `json:"returnCode"`
	ErrorMessage string   `json:"errorMessage"`
	Result       struct{} `json:"result"`
}

// SetBindNode get bind node
func (d *Dao) SetBindNode(ctx context.Context, ip string, port int) error {

	node := &discovery.Node{
		IP:   ip,
		Port: port,
		Host: ip,
	}

	resResp := new(setBindNodeResp)

	resp, err := d.http().R().SetContext(ctx).SetBody(node).Put(d.resCenterHost + setBindNodePath)
	if err != nil {
		return fmt.Errorf("HTTP request err: %s", err)
	}

	if resp.StatusCode() != http.StatusOK {
		return fmt.Errorf("HTTP status %s", resp.Status())
	}

	if err := json.Unmarshal(resp.Body(), resResp); err != nil {
		return err
	}

	if resResp.ReturnCode != 0 {
		return fmt.Errorf("Errcode: %d, Msg: %s", resResp.ReturnCode, resResp.ErrorMessage)
	}

	return nil
}

type nodeRoleResp struct {
	ReturnCode   int    `json:"returnCode"`
	ErrorMessage string `json:"errorMessage"`
	Result       string `json:"result"`
}

// GetNodeRole get node role
func (d *Dao) GetNodeRole(ctx context.Context) (string, error) {

	resResp := new(nodeRoleResp)

	resp, err := d.http().R().SetContext(ctx).Get(d.resCenterHost + getNodeRolePath)
	if err != nil {
		return "", fmt.Errorf("HTTP request err: %s", err)
	}

	if resp.StatusCode() != http.StatusOK {
		if resp.StatusCode() == http.StatusNotFound {
			return "", nil
		}
		return "", fmt.Errorf("HTTP status %s", resp.Status())
	}

	if err := json.Unmarshal(resp.Body(), resResp); err != nil {
		return "", err
	}

	if resResp.ReturnCode != 0 {
		return "", fmt.Errorf("Errcode: %d, Msg: %s", resResp.ReturnCode, resResp.ErrorMessage)
	}

	return resResp.Result, nil
}

// WatchNodeRole watch node role
func (d *Dao) WatchNodeRole(ctx context.Context, id string, cb func(role string) error) error {
	if id == "" {
		return fmt.Errorf("ID is empty")
	}
	url := d.resCenterHost + fmt.Sprintf(subNodeRolePath, id)

	if err := d.SubLongPoll(ctx, url, func(data []byte) error {

		resResp := new(nodeRoleResp)
		if err := json.Unmarshal(data, resResp); err != nil {
			return fmt.Errorf("Resp decode err: %s body: %s", err, data)
		}

		if err := cb(resResp.Result); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}
